////////////////////////////////////////////////////////////////////////////////
//Copyright (c) 2010 Michal Szczepanski - (http://vane.pl)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
package pl.vane.as3relax
{
	import flash.events.EventDispatcher;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	
	import pl.vane.net.services.IURLServiceBase;
	
	/**
	 * Dispatched when data is returned from the CouchDB API,
	 * 
	 * @eventType pl.vane.as3relax.CouchEvent.DATA
	 * 
	 * @langversion ActionScript 3.0
	 * @playerversion Flash 9.0
	 * @tiptext
	 */
	[Event(name="couchevent.data", type="pl.vane.as3relax.CouchEvent")]
	
	/**
	 * Dispatched when data is returned from the CouchDB API,
	 * 
	 * @eventType pl.vane.as3relax.CouchEvent.DATA
	 * 
	 * @langversion ActionScript 3.0
	 * @playerversion Flash 9.0
	 * @tiptext
	 */
	[Event(name="couchevent.fault", type="pl.vane.as3relax.CouchEvent")]
	
	/**
	 * Couch Class is wrapper for CouchDB API.
	 * 
	 * @see http://wiki.apache.org/couchdb/Reference
	 * 
	 * @langversion ActionScript 3.0
	 * @playerversion Flash 9.0
	 * @tiptext
	 */ 
	public class Couch extends EventDispatcher implements ICouch
	{
		/**
		 * @private
		 */
		private var service:CouchService
		
		/**
		 * @private
		 */
		private var _allOrNothing:String;
		
		/**
		 * Constructor.
		 * 
		 * @param url Address to the CouchDB API ex. http://localhost:5984 
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function Couch(url:String)
		{	
			if(!service)
				service = new CouchService(url);
			_allOrNothing = "true";
		}
		
		/**
		 * Sets and returns url to the CouchDB API.
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function get url():String { return service.url; }
		public function set url(url:String):void { service.url = url; }
		
		/**
		 * Sets and returns HTTP Bulk Document API sending property.
		 * 
		 * <p>Whenever using _bulk_docs you can use allOrNothing 
		 * property to perform action to all docs</p>
		 * 
		 * @see http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API
		 * 
		 * @default true
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function get allOrNothing():Boolean { return _allOrNothing == "true"; }
		public function set allOrNothing(value:Boolean):void { value ? _allOrNothing = "true" : _allOrNothing = "false"; }
		
		
		/**
		 * Gets CouchDB document UUID from the database for id string.
		 * 
		 * @param count Number of uuids to get from the CouchDB API default is 100 
		 * @default 100
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function uuids(count:int = 100):void {
			var respoder:CouchResponder = new CouchResponder(CouchEventAction.UUID);
			var request:URLRequest = new URLRequest(service.url+"/_uuids?count="+count);
			request.method = URLRequestMethod.GET;
			makeRequest(request, respoder);
		}
		
		/**
		 * POST request to the database.
		 * 
		 * <p>Whenever you want to send the POST request to the service use
		 * this method.</p>
		 * 
		 * @param docs (optional)Documents in JSON format you want to send to the service. 
		 * @param target Address to the service you want to send the request 
		 *
		 * @see http://wiki.apache.org/couchdb/HTTP_REST_API
		 *  
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function post(docs:String = null, target:String = ""):void {
			var respoder:CouchResponder = new CouchResponder(CouchEventAction.POST);
			var request:URLRequest = new URLRequest(service.url+"/"+target);
			request.data = docs;
			request.method = URLRequestMethod.POST;
			makeRequest(request, respoder);
		}
		/**
		 * GET request to the database.
		 * 
		 * <p>Whenever you want to send the POST request to the service use
		 * this method.</p>
		 * 
		 * @param docs (optional)Documents in JSON format you want to send to the service. 
		 * @param target Address to the service you want to send the request
		 * 
		 * @see http://wiki.apache.org/couchdb/HTTP_REST_API
		 *  
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function get_(docs:String = null, target:String = ""):void {
			var respoder:CouchResponder = new CouchResponder(CouchEventAction.GET);
			var request:URLRequest = new URLRequest(service.url+"/"+target);
			request.data = docs;
			request.method = URLRequestMethod.GET;
			makeRequest(request, respoder);
		}
		
		/**
		 * PUT request to the database.
		 * 
		 * <p>Whenever you want to send the POST request to the service use
		 * this method.</p>
		 * 
		 * @param docs Documents in JSON format you want to send to the service. 
		 * @param target Address to the service you want to send the request
		 * 
		 * @see http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function put(docs:String, target:String):void {
			var respoder:CouchResponder = new CouchResponder(CouchEventAction.PUT);
			var request:URLRequest = new URLRequest(service.url+"/"+target+"/_bulk_docs");
			request.data = '{"all_or_nothing":'+_allOrNothing+', "docs":['+docs+']}';
			request.method = URLRequestMethod.POST;
			makeRequest(request, respoder);
		}
		
		/**
		 * DELETE request to the database.
		 * 
		 * <p>It takes request to the given url and database specified in target property
		 * using bulk docs api so it depends on allOrNothing property.</p>
		 * 
		 * @param docs Document JSON that you want send to the database
		 * @param target Address to the service you want to send the request
		 * 
		 * @see http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function delete_(docs:String, target:String):void {
			var respoder:CouchResponder = new CouchResponder(CouchEventAction.DELETE);
			var request:URLRequest = new URLRequest(service.url+"/"+target+"/_bulk_docs");
			request.data = '{"all_or_nothing":'+_allOrNothing+', "docs":['+docs+']}';
			request.method = URLRequestMethod.POST;
			makeRequest(request, respoder);
		}
		
		/**
		 * Compact database.
		 * 
		 * @param db database to be compacted
		 * @param designName - This compacts the views from the current version of the design document, 
		 * but old versions (from when the design document had a different hash) remain. default<code>null</code>
		 * @param viewCleanup - removes old views from disk default <code>false</code>
		 * 
		 * @see http://wiki.apache.org/couchdb/Compaction
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function compact(db:String, designName:String= null, viewCleanup:Boolean = false):void {
			var respoder:CouchResponder = new CouchResponder(CouchEventAction.COMPACT);
			var requestString:String = service.url+"/"+db;
			if(viewCleanup) {
				requestString += "/_view_cleanup";
			} else {
				requestString += "/_compact";
				if(designName) {
					requestString += "/"+designName;
				}
			}
			var request:URLRequest = new URLRequest(requestString);
			request.method = URLRequestMethod.POST;
			makeRequest(request, respoder);
		}
		
		/**
		 * Replicate database to the given source
		 * 
		 * @param db  - database to be replicated
		 * @param target - full address to the database that you want replicate to
		 * 
		 * @see http://wiki.apache.org/couchdb/Replication 
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function replicate(db:String, target:String):void {
			var respoder:CouchResponder = new CouchResponder(CouchEventAction.REPLICATE);
			var request:URLRequest = new URLRequest(service.url+"/_replicate");
			request.data = '{"source":"'+db+'","target":"'+target+'"}';
			request.method = URLRequestMethod.POST;
			makeRequest(request, respoder);
		}
		
		/**
		 * Get changes feed from the database
		 * 
		 * @param db - database to get the changes feed
		 * @param since - since wtat change get the feed default 0
		 * @param feed - type of feed normal|longpoll|continuous default <code>normal</code>
		 * 
		 * @see pl.vane.as3relax.CouchFeedType
		 * @see http://wiki.apache.org/couchdb/HTTP_database_API
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function changes(db:String, since:uint = 0, feed:String="normal"):void {
			var responder:CouchResponder = new CouchResponder(CouchEventAction.CHANGES);
			// TODO: implement heartbeat=time (milliseconds, default=60000).
			//		 timeout=time (milliseconds, default=60000)
			//		 see http://wiki.apache.org/couchdb/HTTP_database_API
			var request:URLRequest = new URLRequest(service.url+"/"+db+"/_changes?feed="+feed+"&since="+since);
			request.method = URLRequestMethod.POST;
			makeRequest(request, responder);
		}
		
		/**
		 * Makes request to the database can be overriden to handle request for yourself.
		 * 
		 * @param request URLRequest to the database
		 * @param responder CouchResponder that handle the service response
		 * 
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */ 
		public function makeRequest(request:URLRequest, responder:CouchResponder):void {
			var resultHandler:Function = function(resultEvent:CouchEvent):void {
				responder.removeEventListener(CouchEvent.DATA, resultHandler);
				dispatchEvent(resultEvent);
			}
			var faultHandler:Function = function(faultEvent:CouchEvent):void {
				responder.removeEventListener(CouchEvent.FAULT, faultHandler);
				dispatchEvent(faultEvent);
			}
			responder.addEventListener(CouchEvent.DATA, resultHandler);
			responder.addEventListener(CouchEvent.FAULT, faultHandler);
			service.loadFiles([request], responder);
		}
	}
}
